/**
 * Mock Request Router
 * @author Wangtd
 */
const express = require('express');
const router = express.Router();
const os = require('os');
const uuid = require('uuid');
const fs = require('fs');
const path = require('path');
const httpRequest = require('request');
const multipart = require('connect-multiparty');
const svgCaptcha = require('svg-captcha');
const nodemailer = require('nodemailer');
const md5 = require('md5-node');
const AdmZip = require("adm-zip");

const config = require('../config.js');
const util = require('./util.js');
const logger = require('./logger.js');
const template = require('./template.js');
const security = require('./security.js');
const git = require('./git.js');
const httpProxy = require('./proxy.js');

/*======================================== auth ========================================*/

router.get('/mock/auth/captcha', function(request, response) {
    var captcha = svgCaptcha.create({ noise: 2 });
    request.session.captcha = captcha.text;
    response.type('svg');
    response.status(200).send(captcha.data);
});

router.get('/mock/auth/kaptcha', function(request, response) {
    var params = request.query;
    var kaptcha = util.random(6);
    request.session.kaptcha = kaptcha;
    // send email
    sendMail(params.email, '[Mockcat] 注册验证码', template.render('register_kaptcha', {
        kaptcha: kaptcha
    }));
    return response.end();
});

router.get('/mock/auth/rememberme', function(request, response) {
    security.fetchRememberMe(request);
    if (request.session.user) {
        return response.json({
            email: request.session.user.email,
            role: request.session.user.role,
            token: request.session.user.token,
            accessible: true
        });
    } else {
        return response.json({
            accessible: security.hasPermission(request)
        });
    }
});

router.post('/mock/auth/login', function(request, response) {
    var params = request.body;
    logger.info(request, 'User login: email=[%s]', params.email);
    var users = JSON.parse(util.readFileSync(config.workDir + '/users.json'));
    for (var i = 0; i < users.length; i++) {
        if (users[i].email == params.email) {
            if (md5(params.password + users[i].salt) == users[i].password) {
                request.session.user = users[i];
                // remember me
                var value = '', maxAge = 0;
                if (params.rememberMe) {
                    var token = md5(users[i].email + ':' + users[i].password);
                    value = Buffer.from(token).toString('base64');
                    maxAge = 7 * 24* 3600 * 1000;// 7 days
                }
                response.cookie('remember_me', value, { maxAge: maxAge, httpOnly: true });
                return response.json({
                    email: users[i].email,
                    role: users[i].role,
                    token: users[i].token
                });
            }
        }
    }
    response.end('user');
});

router.post('/mock/auth/logout', function(request, response) {
    logger.info(request, 'User logout');
    if (request.session.user) {
        request.session.user = null;
        response.cookie('remember_me', '', { maxAge: 0, httpOnly: true });
    }
    return response.json({
        accessible: security.hasPermission(request)
    });
});

router.post('/mock/auth/password', function(request, response) {
    var params = request.body;
    if (!isValidCaptcha(params.captcha, request)) {
        response.end('captcha');
    } else {
        logger.info(request, 'User edit password');
        if (request.session.user) {
            tryUserLock(function() {
                var filepath = config.workDir + '/users.json',
                    users = JSON.parse(util.readFileSync(filepath));
                for (var i = 0; i < users.length; i++) {
                    if (users[i].email == request.session.user.email) {
                        if (md5(params.password + users[i].salt) == users[i].password) {
                            // update password
                            users[i].salt = md5(uuid.v1());
                            users[i].password = md5(params.newPassword + users[i].salt);
                            users[i].modified = new Date().getTime();
                            // apply for administrator
                            if (config.security.admins.indexOf(users[i].email) != -1) {
                                users[i].role = 'admin';
                            }
                            util.writeFileSync(filepath, JSON.stringify(users, null, 2), true);
                            // clear remember me
                            response.cookie('remember_me', '', { maxAge: 0, httpOnly: true });
                            return response.end();
                        } else {
                            return response.end('password');
                        }
                    }
                }
            }, function() {
                response.end('locked');
            });
        }
        response.end('timeout');
    }
});

router.post('/mock/auth/register', function(request, response) {
    var params = request.body;
    if (!isValidCaptcha(params.kaptcha, request, 'kaptcha')) {
        response.end('kaptcha');
    } else if (params.email.lastIndexOf(config.security.mailDomain) == -1) {
        response.end('domain');
    } else {
        logger.info(request, 'User register: email=[%s]', params.email);
        tryUserLock(function() {
            var filepath = config.workDir + '/users.json',
                users = JSON.parse(util.readFileSync(filepath));
            for (var i = 0; i < users.length; i++) {
                if (users[i].email == params.email) {
                    return response.end('email');
                }
            }
            // create user
            var now = new Date().getTime(),
                salt = md5(uuid.v1());
            users.push({
                email: params.email,
                password: md5(params.password + salt),
                salt: salt,
                role: 'user',
                token: md5(uuid.v1()),
                created: now,
                modified: now
            });
            util.writeFileSync(filepath, JSON.stringify(users, null, 2), true);
            response.end();
        }, function() {
            response.end('locked');
        });
    }
});

router.post('/mock/auth/reset', function(request, response) {
    var params = request.body;
    if (!isValidCaptcha(params.captcha, request)) {
        response.end('captcha');
    } else {
        logger.info(request, 'User reset password: email=[%s]', params.email);
        tryUserLock(function() {
            var filepath = config.workDir + '/users.json',
                users = JSON.parse(util.readFileSync(filepath));
            for (var i = 0; i < users.length; i++) {
                if (users[i].email == params.email) {
                    // reset password
                    var password = util.random(8);
                    users[i].salt = md5(uuid.v1());
                    users[i].password = md5(password + users[i].salt);
                    users[i].token = md5(uuid.v1());
                    users[i].modified = new Date().getTime();
                    util.writeFileSync(filepath, JSON.stringify(users, null, 2), true);
                    // send email
                    sendMail(params.email, '[Mockcat] 密码重置', template.render('reset_password', {
                        password: password
                    }));
                    return response.end();
                }
            }
            response.end('email');
        }, function() {
            response.end('locked');
        });
    }
});

router.post('/mock/token/refresh', function(request, response) {
    var email = request.session.user.email;
    logger.info(request, 'User refresh mock token: email=[%s]', email);
    tryUserLock(function() {
        var filepath = config.workDir + '/users.json',
            users = JSON.parse(util.readFileSync(filepath));
        for (var i = 0; i < users.length; i++) {
            if (users[i].email == email) {
                // refresh token
                var token = md5(uuid.v1());
                users[i].token = token;
                users[i].modified = new Date().getTime();
                util.writeFileSync(filepath, JSON.stringify(users, null, 2), true);
                return response.json({ token: token });
            }
        }
        response.end('email');
    }, function() {
        response.end('locked');
    });
});

/*======================================== mock ========================================*/

router.get('/mock/module/list', function(request, response) {
    var modules = [];
    // module name
    var files = fs.readdirSync(config.mockDir);
    for (var i in files) {
        var filename = files[i];
        var file = fs.statSync(config.mockDir + '/' + filename);
        if(file.isDirectory()) {
            if (filename.indexOf('.') != 0) {// .git
                modules.push({ name: filename });
            }
        }
    }
    // module proxy
    var cfgProxy = path.join(config.workDir, 'proxy.json');
    var proxies = JSON.parse(util.readFileSync(cfgProxy));
    util.each(modules, function(module) {
        var proxy = util.find(proxies, function(item) {
            return item.module == module.name;
        });
        if (proxy) {
            module.port = proxy.port;
        } else {
            module.port = 0;
        }
        module.proxy = config.proxy.enabled;
    });
    response.json(modules);
});

router.put('/mock/module/save', function(request, response) {
    var filepath = path.join(config.mockDir, request.body.module);
    logger.info(request, 'Create module: %s', filepath);
    if(!fs.existsSync(filepath)) {
        fs.mkdirSync(filepath);
        if (config.proxy.enabled) {
            syncHttpProxy('create', { module: request.body.module });
        } else {
            logger.warn(request, 'Mock proxy is disabled!');
        }
    }
    response.json(true);
});

router.put('/mock/module/rename', function(request, response) {
    var params = request.body;
    if (requireModule(params.oldModule)) {
        return response.end('required');
    }
    var oldPath = path.join(config.mockDir, params.oldModule),
        newPath = path.join(config.mockDir, params.newModule);
    logger.info(request, 'Rename module: %s => %s', oldPath, newPath);
    if(fs.existsSync(newPath)) {
        response.json(false);
    } else {
        // git commit
        var email = request.session.user.email;
        var message = util.format('Rename module: %s => %s', path.relative(config.mockDir, oldPath),
                path.relative(config.mockDir, newPath));
        git.commitFiles(function() {
            // rename module
            fs.renameSync(oldPath, newPath);
        }, email, message);
        // mock proxy
        if (config.proxy.enabled) {
            syncHttpProxy('update', {
                modules: [params.oldModule, params.newModule]
            });
        } else {
            logger.warn(request, 'Mock proxy is disabled!');
        }
        response.json(true);
    }
});

router.delete('/mock/module/delete', function(request, response) {
    if (requireModule(request.query.module)) {
        return response.end('required');
    }
    var filepath = path.join(config.mockDir, request.query.module);
    logger.info(request, 'Delete module: %s', filepath);
    // git commit
    var email = request.session.user.email;
    var message = util.format('Delete module: %s', path.relative(config.mockDir, filepath));
    git.commitFiles(function() {
        util.deleteAllSync(filepath);
    }, email, message);
    // mock proxy
    if (config.proxy.enabled) {
        syncHttpProxy('delete', { module: request.query.module });
    } else {
        logger.warn(request, 'Mock proxy is disabled!');
    }
    response.json(true);
});

router.get('/mock/data/tree', function(request, response) {
    var params = request.query;
    var dirpath = path.join(config.mockDir, params.module);
    if(!fs.existsSync(dirpath)) {
        return response.json(false);
    }
    var nodes = {};
    readDirSync(dirpath, nodes);
    response.json(nodes);
});

router.post('/mock/data/save', function(request, response) {
    var params = request.body;
    if (requireModule(params.module)) {
        return response.end('required');
    }
    if (params.type == 'folder') {
        var filepath = path.join(config.mockDir, params.module, params.folder);
        logger.info(request, 'Create directory: %s', filepath);
        if(fs.existsSync(filepath)) {
            response.json(false);
        } else {
            util.mkdirsSync(filepath);
            response.json(true);
        }
    } else if (params.type == 'file') {
        var filepath = path.join(config.mockDir, params.module, params.folder + '/' + params.file);
        logger.info(request, 'Save file: path=%s, size=%d', filepath, params.data.length);
        if(params.created && fs.existsSync(filepath)) {
            response.json(false);
        } else {
            var modified = null;
            var action = '';
            if (fs.existsSync(filepath)) {
                modified = fs.statSync(filepath).mtime.getTime();
                if (modified != params.modified) {
                    return response.end('expired');
                }
                action = 'Update file';
            } else {
                util.mkdirsSync(path.dirname(filepath));
                action = 'Create file';
            }
            // git commit
            var email = request.session.user.email;
            var message = util.format('%s: %s', action, path.relative(config.mockDir, filepath));
            git.commitFiles(function() {
                util.writeFileSync(filepath, params.data, true);
            }, email, message);
            // update modified
            //modified = fs.statSync(filepath).mtime.getTime();
            response.json(/*modified*/true);
        }
    }
});

router.post('/mock/data/rename', function(request, response) {
    var params = request.body;
    if (requireModule(params.module)) {
        return response.end('required');
    }
    var oldPath = path.join(config.mockDir, params.module, params.oldPath),
        newPath = path.join(config.mockDir, params.module, params.newPath);
    logger.info(request, 'Rename path: %s => %s', oldPath, newPath);
    if(fs.existsSync(newPath)) {
        response.json(false);
    } else {
        var action = '';
        if (fs.statSync(oldPath).isDirectory()) {
            action = 'Rename directory';
        } else {
            action = 'Rename file';
        }
        // git commit
        var email = request.session.user.email;
        var message = util.format('%s: %s => %s', action, path.relative(config.mockDir, oldPath),
                path.relative(config.mockDir, newPath));
        git.commitFiles(function() {
            fs.renameSync(oldPath, newPath);
        }, email, message);
        response.json(true);
    }
});

router.delete('/mock/data/delete', function(request, response) {
    var params = request.query;
    if (requireModule(params.module)) {
        return response.end('required');
    }
    var filepath = path.join(config.mockDir, params.module, params.path);
    logger.info(request, 'Delete path: %s', filepath);
    var action = 'Delete file';
    if (fs.statSync(filepath).isDirectory()) {
        action = 'Delete directory';
    }
    // git commit
    var email = request.session.user.email;
    var message = util.format('%s: %s', action, path.relative(config.mockDir, filepath));
    git.commitFiles(function() {
        util.deleteAllSync(filepath);
    }, email, message);
    response.json(true);
});

router.post('/mock/data/copy', function(request, response) {
    var params = request.body;
    if (requireModule(params.module)) {
        return response.end('required');
    }
    var source = path.join(config.mockDir, params.module, params.source);
    var target = path.join(config.mockDir, params.module, params.target);
    // copy & delete
    var email = request.session.user.email;
    if (params.type == 'folder') {
        logger.info(request, 'Copy directory: %s => %s', source, target);
        params.ignored.root = true;
        // git commit
        var message = util.format('Copy directory: %s => %s', path.relative(config.mockDir, source),
                path.relative(config.mockDir, target));
        git.commitFiles(function() {
            copyFolder(source, target, params.ignored);
        }, email, message);
        // move directory
        if (params.movable) {
            logger.info(request, 'Delete directory: %s', source);
            // git commit
            message = util.format('Delete directory: %s', path.relative(config.mockDir, source));
            git.commitFiles(function() {
                util.deleteAllSync(source);
            }, email, message);
        }
    } else if (params.type == 'file') {
        logger.info(request, 'Copy file: %s => %s', source, target);
        var filepath = path.dirname(target);
        if(!fs.existsSync(filepath)) {
            util.mkdirsSync(filepath);
        }
        // git commit
        var message = util.format('Copy file: %s => %s', path.relative(config.mockDir, source),
                path.relative(config.mockDir, target));
        git.commitFiles(function() {
            copyFile(source, target, params.ignored);
        }, email, message);
        // move file
        if (params.movable) {
            logger.info(request, 'Delete file: %s', source);
            // git commit
            message = util.format('Delete file: %s', path.relative(config.mockDir, source));
            git.commitFiles(function() {
                util.deleteAllSync(source);
            }, email, message);
        }
    }
    response.json(true);
});

router.post('/mock/data/import', multipart(), function(request, response) {
    if (request.files) {
        var file = request.files['file'];
        var params = request.query;
        var email = request.session.user.email;
        var filepath = path.join(config.mockDir, params.path);
        logger.info(request, 'Import file: %s', filepath);
        // git commit
        var message = util.format('Import file: %s', path.relative(config.mockDir, filepath));
        git.commitFiles(function() {
            util.copyFileSync(filepath, file.path, params.overwrite);
            fs.unlinkSync(file.path);
        }, email, message);
    }
    response.json(true);
});

router.put('/mock/data/bulk-import', multipart(), function(request, response) {
    if (request.files) {
        var file = request.files['file'];
        var params = request.query;
        var email = request.session.user.email;
        var filepath = path.join(config.mockDir, params.path);
        logger.info(request, 'Import files: %s', filepath);
        // git commit
        var message = util.format('Import files: %s', path.relative(config.mockDir, filepath));
        git.commitFiles(function() {
            var zip = new AdmZip(file.path);
            zip.extractAllTo(filepath, params.overwrite)
            fs.unlinkSync(file.path);
        }, email, message);
    }
    response.json(true);
});

router.post('/mock/data/export', function(request, response) {
    var params = request.query;
    var temppath = path.join(os.tmpdir(), uuid.v1() + '.zip');
    var filepath = path.join(config.mockDir, params.path);
    var dirname = path.basename(filepath);
    var zip = new AdmZip();
    zip.addLocalFolder(filepath, dirname);
    zip.writeZip(temppath);
    // response data.zip
    response.writeHead(200, {
        'content-type': 'application/octet-stream',
        'content-disposition': 'attachment;filename=' + encodeURI(dirname + '.zip')
    });
    fs.createReadStream(temppath/*, 'utf-8'*/).on('end', function() {
        fs.unlinkSync(temppath);
    }).pipe(response);
});

router.post('/mock/git/log-list', function(request, response) {
    var params = request.body;
    var options = {
        parent: params.parent,
        file: params.file,
        sha: params.sha
    };
    git.walkHistory(params.step, options).then(function(history) {
        response.json(history);
    }).catch(function(e) {
        response.status(500).end(e.toString());
    });
});

router.post('/mock/git/log', function(request, response) {
    var params = request.body;
    var file = path.join(config.mockDir, params.file);
    git.walkHistoryFile(file, params.step, params.sha).then(function(history) {
        response.json(history);
    }).catch(function(e) {
        response.status(500).end(e.toString());
    });
});

router.post('/mock/git/file', function(request, response) {
    var params = request.body;
    var file = path.join(config.mockDir, params.file);
    git.readFile(file, params.sha).done(function(data) {
        response.end(data);
    });
});

router.post('/mock/git/diff', function(request, response) {
    var params = request.body;
    var file = path.join(config.mockDir, params.file);
    git.diffCommits(file, params.sha, params.shb).then(function(lines) {
        response.json(lines);
    }).catch(function(e) {
        response.status(500).end(e.toString());
    });
});

router.post('/mock/git/revert', function(request, response) {
    var params = request.body;
    var file = path.join(config.mockDir, params.file);
    var email = request.session.user.email;
    var message = util.format('Revert file: %s', path.relative(config.mockDir, file));
    git.revertFile(file, params.sha, email, message).then(function() {
        response.json(true);
    }).catch(function(e) {
        response.status(500).end(e.toString());
    });
});

router.post('/mock/proxy/load', function(request, response) {
    if (requireModule(request.body.module)) {
        return response.end('required');
    }
    var cfgProxy = path.join(config.workDir, 'proxy.json');
    var modified = fs.statSync(cfgProxy).mtime.getTime();
    var proxies = JSON.parse(util.readFileSync(cfgProxy));
    var proxy = util.find(proxies, function(item) {
        return item.module == request.body.module;
    });
    if (!proxy) {
        proxy = { port: 0, routes: [] };
    }
    response.json({
        modified: modified,
        port: proxy.port,
        ranges: config.proxy.port.ranges,
        offset: config.proxy.port.offset,
        routes: proxy.routes
    });
});

router.put('/mock/proxy/save', function(request, response) {
    var params = request.body;
    if (requireModule(params.module)) {
        return response.end('required');
    }
    var cfgProxy = path.join(config.workDir, 'proxy.json');
    var modified = fs.statSync(cfgProxy).mtime.getTime();
    // check modified
    if (params.modified != modified) {
        return response.end('expired');
    }
    // update proxy
    if (config.proxy.enabled) {
        logger.info(request, 'Update proxy: %s', JSON.stringify(params));
        var proxies = JSON.parse(util.readFileSync(cfgProxy));
        var proxy = util.find(proxies, function(item) {
            return item.module == params.module;
        });
        var newProxy = null;
        if (!proxy) {
            // create a manual proxy
            newProxy = proxy = { module: params.module, port: params.port };
            proxies.push(proxy);
        }
        // update proxy config
        proxy.routes = params.routes;
        if (newProxy || proxy.port != params.port) {
            // check port
            if (util.find(proxies, function(item) {
                return item.module != params.module && item.port == params.port;
            })) {
                return response.end('used');
            }
            var proxyPort = config.proxy.port;
            if (params.port <= proxyPort.ranges[0] || params.port >= proxyPort.ranges[1]) {
                return response.end('offset');
            }
            // update port
            proxy.port = params.port;
            if (proxyPort.offset < proxy.port) {
                proxyPort.offset = proxy.port;
            }
            httpProxy.startServer(proxy.module, proxy.routes, proxy.port);
        } else {
            httpProxy.startServer(proxy.module, proxy.routes);
        }
        util.writeFileSync(cfgProxy, JSON.stringify(proxies, null, 2), true);
        // update modified
        modified = fs.statSync(cfgProxy).mtime.getTime();
    } else {
        logger.warn(request, 'Mock proxy is disabled!');
    }
    response.json(modified);
});

router.post('/mock/blog/upload', multipart(), function(request, response) {
    var filename = '';
    if (request.files) {
        var file = request.files['file'];
        filename = md5(uuid.v1()) + path.extname(file.name);
        var filepath = path.join(config.blogDir, 'images', filename);
        // copy file
        fs.createReadStream(file.path).pipe(fs.createWriteStream(filepath));
        // delete file
        fs.unlinkSync(file.path);
    }
    response.end(filename);
});

router.post('/mock/blog/category', function(request, response) {
    tryBlogLock(function() {
        var filepath = config.blogDir + '/blogs.json',
            blogs = JSON.parse(util.readFileSync(filepath)),
            categories = blogs.categories;
        var category = null;
        // check category
        if (util.find(categories, function(category) {
            return category.id != request.body.id && category.name == request.body.category;
        })) {
            return response.end('existed');
        }
        if (request.body.id) {
            // update category
            for (var i = 0; i < categories.length; i++) {
                if (categories[i].id == request.body.id) {
                    category = categories[i];
                    categories[i].name = request.body.category;
                    break;
                }
            }
        } else {
            // insert category
            category = {
                id: new Date().getTime().toString(16),
                name: request.body.category
            };
            categories.push(category);
        }
        // order category
        if (categories.indexOf(category) != request.body.order) {
            var ordered = false, orderedCategories = [];
            for (var i = 0; i < categories.length; i++) {
                if (i == request.body.order) {
                    ordered = true;
                    orderedCategories.push(category);
                }
                if (categories[i].id != category.id) {
                    orderedCategories.push(categories[i]);
                }
            }
            if (!ordered) {
                orderedCategories.push(category);
            }
            blogs.categories = orderedCategories;
        }
        // update category
        util.writeFileSync(filepath, JSON.stringify(blogs, null, 2), true);
        // response category
        response.json(category);
    }, function() {
        response.end('locked');
    });
});

router.post('/mock/blog/publish', function(request, response) {
    tryBlogLock(function() {
        var blogid = request.body.blogid,
            blog = null,
            category = null;
        var blogs = JSON.parse(util.readFileSync(config.blogDir + '/blogs.json'));
        // category
        if (request.body.category) {
            category = request.body.category;
        } else {
            var oCategory = util.find(blogs.categories, function(category) {
                return category.name == request.body.categoryName;
            });
            if (oCategory) {
                category = oCategory.id;
            } else {
                category = new Date().getTime().toString(16);
                blogs.categories.push({
                    id: category,
                    name: request.body.categoryName
                });
            }
        }
        // article
        if (blogid) {
            for (var i = 0; i < blogs.articles.length; i++) {
                if (blogs.articles[i].id == blogid) {
                    blog = blogs.articles[i];
                    if (request.body.modified !== blog.modified) {
                        return response.end('expired');
                    }
                    // update blog
                    blogs.articles[i].title = request.body.title;
                    blogs.articles[i].category = category;
                    blogs.articles[i].tags = request.body.tags;
                    blogs.articles[i].snapshot = request.body.snapshot;
                    blogs.articles[i].modifier = request.session.user.email;
                    blogs.articles[i].modified = new Date().getTime();
                    break;
                }
            }
        }
        if (blog == null) {
            var oBlog = util.find(blogs.articles, function(article) {
                return article.title == request.body.title;
            });
            if (oBlog) {
                return response.end('existed');
            } else {
                var now = new Date().getTime();
                blog = {
                    id: md5(uuid.v1()),
                    title: request.body.title,
                    category: category,
                    tags: request.body.tags,
                    snapshot: request.body.snapshot,
                    creator: request.session.user.email,
                    created: now,
                    modifier: request.session.user.email,
                    modified: now
                };
                blogs.articles.unshift(blog);
            }
        }
        logger.info(request, 'Publish blog: id=%s, size=%d', blog.id, request.body.content.length);
        // update blogs
        util.writeFileSync(config.blogDir + '/blogs.json', JSON.stringify(blogs, null, 2), true);
        // update article
        util.writeFileSync(config.blogDir + '/articles/' + blog.id + '.md', request.body.content, true);
        // response blog
        response.json(blog);
    }, function() {
        response.end('locked');
    });
});

router.delete('/mock/blog/delete', function(request, response) {
    tryBlogLock(function() {
        var blogs = JSON.parse(util.readFileSync(config.blogDir + '/blogs.json'));
        // category
        if (request.query.category) {
            // check category
            var oArticle = util.find(blogs.articles, function(article) {
                return article.category == request.query.category;
            });
            if (oArticle) {
                return response.end('used');
            }
            // delete category
            for (var i = 0; i < blogs.categories.length; i++) {
                if (blogs.categories[i].id == request.query.category) {
                    blogs.categories.splice(i, 1);
                    break;
                }
            }
        }
        // article
        if (request.query.article) {
            for (var i = 0; i < blogs.articles.length; i++) {
                if (blogs.articles[i].id == request.query.article) {
                    logger.info(request, 'Delete blog: id=[%s]', blogs.articles[i].id);
                    blogs.articles.splice(i, 1);
                    break;
                }
            }
        }
        // update blogs
        util.writeFileSync(config.blogDir + '/blogs.json', JSON.stringify(blogs, null, 2), true);
        response.json(true);
    }, function() {
        response.end('locked');
    });
});

/*======================================== tool ========================================*/

router.post('/mock/file/read', multipart(), function(request, response) {
    var content = '';
    if (request.files) {
        var file = request.files['file'];
        content = util.readFileSync(file.path);
        // delete file
        fs.unlinkSync(file.path);
    }
    response.writeHead(200, {
        'content-type': 'text/plain;charset=utf-8'
    });
    response.end(content);
});

router.post('/mock/file/write', function(request, response) {
    var filename = request.body.filename,
        filepath = path.join(os.tmpdir(), uuid.v1() + path.extname(filename));
    // save file
    util.writeFileSync(filepath, request.body.content, true);
    response.writeHead(200, {
        'content-type': 'application/octet-stream',
        'content-disposition': 'attachment;filename=' + encodeURI(filename)
    });
    // response stream
    fs.createReadStream(filepath, 'utf-8').on('end', function() {
        fs.unlinkSync(filepath);
    }).pipe(response);
});

router.post('/mock/test/send', function(request, response) {
    var params = request.body;
    var options = {
        url: params.url,
        method: params.method,
        headers: {
            'encoding': 'UTF-8',
            'Connection': 'keep-alive',
            'Content-Type': 'application/' + params.type,
            'Cookie': request.headers.cookie
        },
        timeout: 30000 // 30s
    };
    // headers
    if (params.headers) {
        for (var name in params.headers) {
            options.headers[name] = params.headers[name];
        }
    }
    // body
    if (params.data != null) {
        options['body'] = params.data;
    }
    httpRequest(options).on('error', function(error) {
        console.log(error);
        return response.end('The server has disconnected or the connection has timed out.');
    }).pipe(response);
});

router.post('/mock/test/request', multipart(), function(request, response) {
    var url = request.body['__url'],
        method = request.body['__method'],
        headers = JSON.parse(request.body['__headers']),
        cookies = JSON.parse(request.body['__cookies']),
        body = request.body['__body'];

    var options = {
        url: url,
        method: method,
        headers: {
            'encoding': 'UTF-8',
            'Connection': 'keep-alive',
             // cookies
            'Cookie': rebuildCookies(request.headers.cookie, cookies)
        },
        timeout: 900000 // 15minutes
    };

    // headers
    for (var name in headers) {
        options.headers[name] = headers[name];
    }

    // body
    if (method != 'GET' && method != 'HEAD') {
        switch (headers['Content-Type']) {
            case 'multipart/form-data':
                var formData = {};
                if (body) {
                    var fields = JSON.parse(body);
                    for (var name in fields) {
                        formData[name] = fields[name];
                    }
                }
                if (request.files) {
                    for (var name in request.files) {
                        var file = request.files[name];
                        formData[name] = {
                            value: fs.createReadStream(file.path),
                            options: {
                                filename: file.originalFilename,
                                contentType: file.type
                            }
                        };
                    }
                }
                options['formData'] = formData;
                break;
            case 'application/json':
                options['json'] = true;
                if (body) {
	                options['body'] = JSON.parse(body);
                }
                break;
            case 'application/x-www-form-urlencoded':
                if (body) {
	                options['form'] = JSON.parse(body);
                }
                break;
            case 'text/plain':
            case 'application/javascript':
            case 'application/xml':
            case 'text/xml':
            case 'text/html':
                options['body'] = body;
                break;
            default:
                options['body'] = body;
        }
    }

    httpRequest(options).on('error', function(error) {
        console.log(error);
        response.writeHead(504, {
            'content-type': 'text/plain;charset=utf-8'
        });
        return response.end('The server has disconnected or the connection has timed out.');
    }).on('response', function(response) {
        // set cookies
        response.headers['x-set-cookies'] = (response.headers['set-cookie'] || []).join('@');
    }).on('end', function() {
        // delete files
        if (request.files) {
            for (var name in request.files) {
                fs.unlinkSync(request.files[name].path);
            }
        }
    }).pipe(response);
});

function rebuildCookies(cookie, cookies) {
    if (Object.keys(cookies).length == 0) {
        return cookie;
    } else {
        var pattern = /([^=]+)=([^;]+);?\s*/g,
            result = null;
        while ((result = pattern.exec(cookie)) != null) {
            cookies[result[1]] = result[2];
        }
        // cookies to string
        var values = [];
        for (var name in cookies) {
            values.push(name + '=' + cookies[name]);
        }
        return values.join('; ');
    }
}

function copyFolder(source, target, ignored) {
    var copyable = ignored.root || ignored.folder;
    if (!fs.existsSync(target)) {
        copyable = true;
        util.mkdirsSync(target);
    }
    if (copyable) {
        ignored.root = false;
        var files = fs.readdirSync(source);
        for (var i in files) {
            var filename = files[i];
            var curpath = path.join(source, filename);
            var tgtpath = path.join(target, filename);
            if(fs.statSync(curpath).isDirectory()) {
                // copy folder
                copyFolder(curpath, tgtpath, ignored);
            } else {
                // copy file
                copyFile(curpath, tgtpath, ignored);
            }
        }
    }
}

function copyFile(source, target, ignored) {
    if (!fs.existsSync(target) || ignored.file) {
        //fs.createReadStream(source).pipe(fs.createWriteStream(target));
        util.copyFileSync(target, source, true);
    }
}

function readDirSync(filedir, srcnode) {
    var children = [];
    var files = fs.readdirSync(filedir);
    for (var i in files) {
        var filename = files[i];
        var subnode = {};
        subnode.name = filename;
        var file = fs.statSync(filedir + '/' + filename);
        if(file.isDirectory()) {
            subnode.leaf = false;
            readDirSync(filedir + '/' + filename, subnode);
        } else {
            subnode.leaf = true;
        }
        children.push(subnode);
    }
    srcnode.children = children;
}

function isValidCaptcha(captcha, request, sessionKey) {
    if (!sessionKey) {
        sessionKey = 'captcha';
    }
    return new RegExp('^' + captcha + '$', 'i').test(request.session[sessionKey]);
}

function requireModule(module) {
    if (!module) {
        return true;
    } else {
        var dirpath = path.join(config.mockDir, module);
        if(!fs.existsSync(dirpath)) {
            return true;
        }
    }
    return false;
}

function sendMail(account, subject, html) {
    var transport = nodemailer.createTransport(config.mailConfig);
    transport.sendMail({
        from: config.mailConfig.auth.user,
        to: account,
        subject: subject,
        html: html
    }, function(error) {
        if (error) {
            console.log(error);
        } else {
            logger.info(request, 'Send mail success: account=[%s]', account);
        }
    });
}

function syncHttpProxy(action, options) {
    var cfgProxy = config.workDir + '/proxy.json';
    var proxies = JSON.parse(util.readFileSync(cfgProxy));
    switch (action) {
        case 'create':
            var proxy = {
                module: options.module,
                port: ++config.proxy.port.offset,
                routes: [{ from: '/', to: '/' + options.module + '/', enabled: true }]
            };
            proxies.push(proxy);
            httpProxy.startServer(proxy.module, proxy.routes, proxy.port);
            break;
        case 'update':
            var proxy = util.find(proxies, function(item) {
                return item.module == options.modules[0];
            });
            if (proxy) {
                httpProxy.destroyServer(proxy.module);
                proxy.module = options.modules[1];
                httpProxy.startServer(proxy.module, proxy.routes, proxy.port);
            }
            break;
        case 'delete':
            var proxy = util.find(proxies, function(item) {
                return item.module == options.module;
            });
            if (proxy) {
                proxies.splice(proxies.indexOf(proxy), 1);
                httpProxy.destroyServer(proxy.module);
            }
            break;
    }
    util.writeFileSync(cfgProxy, JSON.stringify(proxies, null, 2), true);
}

function tryUserLock(resolve, reject) {
    tryLock(resolve, reject, '/users.lock');
}

function tryBlogLock(resolve, reject) {
    tryLock(resolve, reject, '/blogs.lock');
}

function tryLock(resolve, reject, lock) {
    var enabled = false,
        lockpath = config.workDir + lock;
    try {
        if(fs.existsSync(lockpath)) {
            reject();
        } else {
            try {
                fs.writeFileSync(lockpath, 'locked!', { flag: 'wx' });
                enabled = true;
                resolve();
            } catch (e) {
                reject();
            }
        }
    } finally {
        if (enabled) {
            fs.unlinkSync(lockpath);
        }
    }
}

module.exports = router;
